package com.matrix.quartz.core;

import java.util.Date;
import java.util.Map;

import com.matrix.quartz.contance.QuartzContance;
import org.apache.commons.lang.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.matrix.core.constance.SystemErrorCode;
import com.matrix.core.exception.GlobleException;
import com.matrix.core.tools.LogUtil;
import com.matrix.core.tools.ModelUtils;
import com.matrix.core.tools.UUIDUtil;
import com.matrix.quartz.bean.QuartzJobDefinition;
import com.matrix.quartz.dao.QuartzJobDefinitionDao;

/**
 * 定时任务核心类
 * 
 * @author zhangheng
 * @email 512061637@qq.com
 * @date 2019年1月14日
 */
@Component
public class QuartzCoreUtil {

	//@Autowired
	private Scheduler scheduler;

	//@Autowired
	private QuartzJobDefinitionDao jobDefinitionDao;

	/**
	 * 修改定时任务
	 *
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月16日
	 * @param jobDefinition
	 * @return
	 * @throws SchedulerException
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean modifyJob(QuartzJobDefinition oldJobDefinition, QuartzJobDefinition newJobDefinition)
			throws SchedulerException {
		// 先判断是否修改了
		int i = 0;
		Map<String, Object> modifyMap = null;
		try {
			if (ModelUtils.isModified(oldJobDefinition, newJobDefinition)) {
				modifyMap = ModelUtils.comparePojo2Map(oldJobDefinition, newJobDefinition);
			}
		} catch (Exception e) {
			throw new GlobleException(SystemErrorCode.DATA_UPDATE_FAIL, e, newJobDefinition);
		}
		if (modifyMap.size() > 0) {
			modifyMap.put(QuartzContance.JOB_JDID_KEY, oldJobDefinition.getJdId());
			// 判断改变了哪些 只能修改运行的时间表达式
			if (modifyMap.containsKey("jdCron")) {
				// 只能修改时间表达式
				TriggerKey triggerKey = TriggerKey.triggerKey(oldJobDefinition.getJdJobName(),
						oldJobDefinition.getJdJobGroup());
				// 新的触发器
				CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(newJobDefinition.getJdCron())
						.withMisfireHandlingInstructionDoNothing();
				CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
						.withSchedule(cronScheduleBuilder).build();
				scheduler.rescheduleJob(triggerKey, cronTrigger);
			}
			jobDefinitionDao.updateByMap(modifyMap);
		}
		return true;
	}

	/**
	 * 根据任务编码创建新的定时任务
	 *
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月14日
	 * @param jobCode  自定义的任务编码
	 * @param jobClass 任务执行类
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean createCronJob(QuartzJobDefinition jobDefinition) {
		LogUtil.info("创建Cron定时任务,任务编码#{},任务执行类#{}", jobDefinition.getJdCode(), jobDefinition.getClass());

		// 获得时间表达式
		String cron = jobDefinition.getJdCron();
		// 任务名和任务组
		String jobName = UUIDUtil.getRandomID() + jobDefinition.getJdCode();
		String jobGroup = jobDefinition.getJdCode() + QuartzContance.JOB_DEFAULT_GROUP;

		// 先将定时任务信息存入业务表中
		jobDefinition.setJdJobName(jobName);
		jobDefinition.setJdJobGroup(jobGroup);
		jobDefinition.setJdStatus(QuartzContance.JOB_STATUS_1);
		jobDefinitionDao.insert(jobDefinition);
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		JobKey jobKey = new JobKey(jobName, jobGroup);

		// 验证该任务是否已存在
		if (checkExists(jobName, jobGroup)) {
			LogUtil.info("该任务已存在,新建任务失败, jobGroup:{}, jobName:{}", jobGroup, jobName);
			return false;
		}

		// CronTrigger : TriggerKey + cronExpression //
		// withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度
		CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron)
				.withMisfireHandlingInstructionDoNothing();
		CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
				.build();

		// JobDetail : jobClass
		// 获取任务类
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		Class<? extends Job> jobClass = null;
		try {
			jobClass = (Class<? extends Job>) loader.loadClass(jobDefinition.getJdClass());
		} catch (ClassNotFoundException e) {
			// 获取类失败 说明执行类不存在
			LogUtil.error("定时任务创建时获取任务执行类失败,类路径#{}", jobDefinition.getJdClass());
			throw new GlobleException("该执行类不存在!");
		}
		if (jobClass == null) {
			throw new GlobleException("该执行类不存在!");
		}

		JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
		// 加入参数
		jobDetail.getJobDataMap().put(QuartzContance.JOB_JDID_KEY, jobDefinition.getJdId().toString());
		// 判断其它参数
		if (StringUtils.isNotBlank(jobDefinition.getJdParam1())) {
			jobDetail.getJobDataMap().put(QuartzContance.JOB_PARAM_KEY1, jobDefinition.getJdId().toString());
			if (StringUtils.isNotBlank(jobDefinition.getJdParam2())) {
				jobDetail.getJobDataMap().put(QuartzContance.JOB_PARAM_KEY2, jobDefinition.getJdId().toString());
			}
		}

		// schedule : jobDetail + cronTrigger
		try {
			Date date = scheduler.scheduleJob(jobDetail, cronTrigger);
		} catch (SchedulerException e) {
			// 创建任务失败
			LogUtil.error("创建定时任务#{}失败！", e, jobDefinition.getJdName());
			throw new GlobleException("创建定时任务失败！");
		}
		LogUtil.info("创建Cron任务成功#{}", jobDefinition.getJdJobName());
		return true;
	}

	/**
	 * 恢复定时任务
	 * 
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月16日
	 * @param oldJobDefinition
	 * @return
	 * @throws SchedulerException
	 */
	public boolean resumeJob(QuartzJobDefinition jobDefinition) throws SchedulerException {
		if (QuartzContance.JOB_STATUS_2.equals(jobDefinition.getJdStatus())) {
			// 暂停下的才启动
			JobKey jobKey = new JobKey(jobDefinition.getJdJobName(), jobDefinition.getJdJobGroup());
			scheduler.resumeJob(jobKey);
			// 同时更新数据状态
			QuartzJobDefinition updateJobDefinition = new QuartzJobDefinition();
			updateJobDefinition.setJdId(jobDefinition.getJdId());
			updateJobDefinition.setJdStatus(QuartzContance.JOB_STATUS_1);
			jobDefinitionDao.updateByModel(updateJobDefinition);
		}
		return true;
	}

	/**
	 * 暂停定时任务
	 * 
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月16日
	 * @param oldJobDefinition
	 * @return
	 * @throws SchedulerException
	 */
	public boolean pauseJob(QuartzJobDefinition jobDefinition) throws SchedulerException {
		if (QuartzContance.JOB_STATUS_1.equals(jobDefinition.getJdStatus())) {
			// 运行下的才启动
			JobKey jobKey = new JobKey(jobDefinition.getJdJobName(), jobDefinition.getJdJobGroup());
			scheduler.pauseJob(jobKey);
			// 同时更新数据状态
			QuartzJobDefinition updateJobDefinition = new QuartzJobDefinition();
			updateJobDefinition.setJdId(jobDefinition.getJdId());
			updateJobDefinition.setJdStatus(QuartzContance.JOB_STATUS_2);
			jobDefinitionDao.updateByModel(updateJobDefinition);
		}
		return true;
	}

	/**
	 * 删除定时任务
	 * 
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月16日
	 * @param jobDefinition
	 * @return
	 * @throws SchedulerException
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean deleteJob(QuartzJobDefinition jobDefinition) throws SchedulerException {
		JobKey jobKey = new JobKey(jobDefinition.getJdJobName(), jobDefinition.getJdJobGroup());
		// 检查任务是否存在
		if (scheduler.checkExists(jobKey)) {
			scheduler.deleteJob(jobKey);
		}
		// 删除任务
		jobDefinitionDao.deleteById(jobDefinition.getJdId());
		return true;
	}

	/**
	 * 新建任务时检查该任务是否存在
	 * 
	 * @author zhangheng
	 * @email 512061637@qq.com
	 * @date 2019年1月14日
	 * @param jobName
	 * @param jobGroup
	 * @return
	 */
	public boolean checkExists(String jobName, String jobGroup) {
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		boolean flag = false;
		try {
			flag = scheduler.checkExists(triggerKey);
		} catch (Exception e) {
			LogUtil.error("检查任务时异常", e);
			flag = false;
		}
		return flag;
	}
}
